package indv.Cning.cfengsecuritydemo.controller;

import indv.Cning.cfengsecuritydemo.domain.Resp;
import indv.Cning.cfengsecuritydemo.domain.UserDto;
import indv.Cning.cfengsecuritydemo.jwt.JwtTokenUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

/**
 * @author Cfeng
 * @date 2022/7/24
 * 主要完成登录认证和签发JWT【登录认证都是spring security完成】
 */

@RestController
@RequiredArgsConstructor
public class JwtAuthenticationController {
    //securityConfig中配置过
    private final AuthenticationManager authenticationManager;

    private final JwtTokenUtil jwtTokenUtil;

    private final UserDetailsService userDetailsService;

    @PostMapping("/authenticate")
    public Resp<?> createAuthenticationToken(@RequestBody UserDto authenticationUser, HttpServletResponse response) throws Exception {
        this.authenticate(authenticationUser.getUserName(),authenticationUser.getUserPwd());
        //进行认证和授权,获得登录用户
        UserDetails userDetails =  userDetailsService.loadUserByUsername(authenticationUser.getUserName());
        //进行jwt签发,生成一个jwtUser的token
        String token = jwtTokenUtil.generateToken(userDetails);
        //通过resp将token写入
        this.setCookie(token,response);
        return Resp.ok(token); //响应token信息
    }

    /**
     * 不是在此类中进行验证用户名和密码，而是委托给Spring security中的AuthenticationManager
     */
    private void authenticate(String username, String password) throws Exception {
        //springSecurity依靠此管理器进行认证
        try {
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username,password));
        } catch (DisabledException e) {
            throw new Exception("USER_DISABLED",e);
        } catch (BadCredentialsException e) {
            throw new Exception("INVALID_CREDENTIALS",e);
        }
    }

    /**
     * 将JWT写入cookie，减少客户端对Header的操作
     */
    private void setCookie(String token, HttpServletResponse response) {
        Cookie cookie = new Cookie("jwt",token);
        //cookie响应给客户端，response即可
        response.addCookie(cookie);
    }
}
